// Copyright (C) 1986-1998 by Symantec
// Copyright (C) 2000-2009 by Digital Mars
// All Rights Reserved
// http://www.digitalmars.com
// Written by Walter Bright
/*
 * This source file is made available for personal use
 * only. The license is in /dmd/src/dmd/backendlicense.txt
 * For any other uses, please contact Digital Mars.
 */

#if !SPP

#include	<stdio.h>
#include	<string.h>
#include	<stdlib.h>
#include	<time.h>

#include	"cc.h"
#include	"global.h"
#include	"oper.h"
#include	"el.h"
#include	"go.h"
#include	"type.h"

#if TARGET_MAC
#include	"TG.h"
#endif

static char __file__[] = __FILE__;	/* for tassert.h		*/
#include	"tassert.h"

/****************************
 * Terminate use of globals.
 */

void go_term()
{
    vec_free(defkill);
    vec_free(starkill);
    vec_free(vptrkill);
#if TX86
    util_free(expnod);
    util_free(expblk);
    util_free(defnod);
#else
    MEM_BEF_FREE(expnod);
    MEM_BEF_FREE(expblk);
    MEM_BEF_FREE(defnod);
#endif
}

#if DEBUG
			// to print progress message and current trees set to 
			// DEBUG_TREES to 1 and uncomment next 2 lines
#define DEBUG_TREES 0
#if DEBUG_TREES
void dbg_optprint(char *);
#else
			// to print progress message, undo comment
#define dbg_optprint(c) // dbg_printf(c)
#endif
#else
#define dbg_optprint(c)
#endif

#if TX86
/**************************************
 * Parse optimizer command line flag.
 * Input:
 *	cp	flag string
 * Returns:
 *	0	not recognized
 *	!=0	recognized
 */

int go_flag(char *cp)
{   unsigned i;
    unsigned flag;

    enum GL	// indices of various flags in flagtab[]
    {
	GLO,GLall,GLcnp,GLcp,GLcse,GLda,GLdc,GLdv,GLli,GLliv,GLlocal,GLloop,
	GLnone,GLo,GLreg,GLspace,GLspeed,GLtime,GLtree,GLvbe,GLMAX
    };
    static const char *flagtab[] =
    {	"O","all","cnp","cp","cse","da","dc","dv","li","liv","local","loop",
	"none","o","reg","space","speed","time","tree","vbe"
    };
    static mftype flagmftab[] =
    {	0,MFall,MFcnp,MFcp,MFcse,MFda,MFdc,MFdv,MFli,MFliv,MFlocal,MFloop,
	0,0,MFreg,0,MFtime,MFtime,MFtree,MFvbe
    };

    i = GLMAX;
    assert(i == arraysize(flagtab));
    assert(i == arraysize(flagmftab));

    //printf("go_flag('%s')\n", cp);
    flag = binary(cp + 1,flagtab,GLMAX);
    if (mfoptim == 0 && flag != -1)
	mfoptim = MFall & ~MFvbe;

    if (*cp == '-')			/* a regular -whatever flag	*/
    {					/* cp -> flag string		*/
	switch (flag)
	{
	    case GLall:
	    case GLcnp:
	    case GLcp:
	    case GLdc:
	    case GLda:
	    case GLdv:
	    case GLcse:
	    case GLli:
	    case GLliv:
	    case GLlocal:
	    case GLloop:
	    case GLreg:
	    case GLspeed:
	    case GLtime:
	    case GLtree:
	    case GLvbe:
		mfoptim &= ~flagmftab[flag];	/* clear bits	*/
		break;
	    case GLo:
	    case GLO:
	    case GLnone:
		mfoptim |= MFall & ~MFvbe;	// inverse of -all
		break;
	    case GLspace:
		mfoptim |= MFtime;	/* inverse of -time	*/
		break;
	    case -1:			/* not in flagtab[]	*/
		goto badflag;
	    default:
		assert(0);
	}
    }
    else if (*cp == '+')		/* a regular +whatever flag	*/
    {				/* cp -> flag string		*/
	switch (flag)
	{
	    case GLall:
	    case GLcnp:
	    case GLcp:
	    case GLdc:
	    case GLda:
	    case GLdv:
	    case GLcse:
	    case GLli:
	    case GLliv:
	    case GLlocal:
	    case GLloop:
	    case GLreg:
	    case GLspeed:
	    case GLtime:
	    case GLtree:
	    case GLvbe:
		mfoptim |= flagmftab[flag];	/* set bits	*/
		break;
	    case GLnone:
		mfoptim &= ~MFall;	/* inverse of +all	*/
		break;
	    case GLspace:
		mfoptim &= ~MFtime;	/* inverse of +time	*/
		break;
	    case -1:			/* not in flagtab[]	*/
		goto badflag;
	    default:
		assert(0);
	}
    }
    if (mfoptim)
    {
	mfoptim |= MFtree | MFdc;	// always do at least this much
	config.flags4 |= (mfoptim & MFtime) ? CFG4speed : CFG4space;
    }
    else
    {
	config.flags4 &= ~CFG4optimized;
    }
    return 1;			// recognized

badflag:
    return 0;
}

#endif

#if DEBUG_TREES
void dbg_optprint(char *title)
{
    block *b;
    for (b = startblock; b; b = b->Bnext)
	if (b->Belem)
	{
	    dbg_printf("%s\n",title);
	    elem_print(b->Belem);
	}
}
#endif

/****************************
 * Optimize function.
 */

void optfunc()
{
#if !HTOD
    block *b;
    int iter;		// iteration count
    clock_t starttime;

    cmes ("optfunc()\n");
    dbg_optprint("optfunc\n");
#ifdef DEBUG
    if (debugb)
    {
	dbg_printf("................Before optimization.........\n");
	WRfunc();
    }
#endif
    iter = 0;

    if (localgot)
    {   // Initialize with:
	//	localgot = OPgot;
	elem *e = el_long(TYnptr, 0);
	e->Eoper = OPgot;
	e = el_bin(OPeq, TYnptr, el_var(localgot), e);
	startblock->Belem = el_combine(e, startblock->Belem);
    }

    // Some functions can take enormous amounts of time to optimize.
    // We try to put a lid on it.
    starttime = clock();
    do
    {
	//printf("iter = %d\n", iter);
#if TX86
	//assert(++iter < 80);		/* infinite loop check		 */
	assert(++iter < 200);		/* infinite loop check		 */
#else
     L1:
#endif
#if MARS
	util_progress();
#else
	file_progress();
#endif

	//printf("optelem\n");
	/* canonicalize the trees	 */
	for (b = startblock; b; b = b->Bnext)
	    if (b->Belem)
	    {
#if DEBUG
		if(debuge)
		{
		    dbg_printf("before\n");
		    elem_print(b->Belem);
		    //el_check(b->Belem);
		}
#endif
		b->Belem = doptelem(b->Belem,bc_goal[b->BC] | GOALagain);
#if DEBUG
		if(0 && debugf)
		{
		    dbg_printf("after\n");
		    elem_print(b->Belem);
		}
#endif
	    }
	//printf("blockopt\n");
#if TX86
	if (mfoptim & MFdc)
	    blockopt(0);		// do block optimization
	out_regcand(&globsym);		// recompute register candidates
	changes = 0;			/* no changes yet		 */
	if (mfoptim & MFcnp)
	    constprop();		/* make relationals unsigned	 */
	if (mfoptim & (MFli | MFliv))
#else
	if (config.optimized && (mfoptim & MFdc))
	    blockopt(0);		// do block optimization

	dbg_optprint("blockopt\n");
	out_regcand();			// recompute register candidates
	changes = 0;			/* no changes yet		 */

	dbg_optprint("constprop\n");
	if (config.optimized && (mfoptim & MFcnp))
	    constprop();		/* make relationals unsigned	 */

	dbg_optprint("loopopt\n");
	if (config.optimized && (mfoptim & (MFli | MFliv)))
#endif
	    loopopt();			/* remove loop invariants and	 */
					/* induction vars		 */
					/* do loop rotation		 */
	else
	    for (b = startblock; b; b = b->Bnext)
		b->Bweight = 1;
	dbg_optprint("boolopt\n");
#if TX86
	if (mfoptim & MFcnp)
	    boolopt();			// optimize boolean values
	if (changes && mfoptim & MFloop && (clock() - starttime) < 30 * CLOCKS_PER_SEC)
	    continue;

	if (mfoptim & MFcnp)
	    constprop();		/* constant propagation		 */
	if (mfoptim & MFcp)
	    copyprop();			/* do copy propagation		 */

	/* Floating point constants and string literals need to be
	 * replaced with loads from variables in read-only data.
	 * This can result in localgot getting needed.
	 */
	symbol *localgotsave = localgot;
	for (b = startblock; b; b = b->Bnext)
	{
	    if (b->Belem)
	    {   b->Belem = doptelem(b->Belem,bc_goal[b->BC] | GOALstruct);
		if (b->Belem)
		    b->Belem = el_convert(b->Belem);
	    }
	}
	if (localgot != localgotsave)
	{   /* Looks like we did need localgot, initialize with:
	     *	localgot = OPgot;
	     */
	    elem *e = el_long(TYnptr, 0);
	    e->Eoper = OPgot;
	    e = el_bin(OPeq, TYnptr, el_var(localgot), e);
	    startblock->Belem = el_combine(e, startblock->Belem);
	}

	/* localize() is after localgot, otherwise we wind up with
	 * more than one OPgot in a function, which mucks up OSX
	 * code generation which assumes at most one (localgotoffset).
	 */
	if (mfoptim & MFlocal)
	    localize();			// improve expression locality
	if (mfoptim & MFda)
	    rmdeadass();		/* remove dead assignments	 */

	cmes2 ("changes = %d\n", changes);
	if (!(changes && mfoptim & MFloop && (clock() - starttime) < 30 * CLOCKS_PER_SEC))
	    break;
    } while (1);
    cmes2("%d iterations\n",iter);
    if (mfoptim & MFdc)
	blockopt(1);			// do block optimization
#else
	if (config.optimized && (mfoptim & MFcnp))
	    boolopt();			// optimize boolean values
	util_progress();
	if (changes)
	    goto L1;

	dbg_optprint("constprop\n");
	if (config.optimized && (mfoptim & MFcnp))
	    constprop();		/* constant propagation		 */

	dbg_optprint("copyprop\n");
	if (config.optimized && (mfoptim & MFcp))
	    copyprop();			/* do copy propagation		 */

	dbg_optprint("localize\n");
	if (config.optimized && (mfoptim & MFlocal))
	    localize();			// improve expression locality

	dbg_optprint("rmdeadass\n");
	if (config.optimized && (mfoptim & MFda))
	    rmdeadass();		/* remove dead assignments	 */
	cmes2 ("changes = %d\n", changes);
	iter++;
	assert (iter < 80);		/* infinite loop check		 */
    } while (changes && (config.optimized) && (mfoptim & MFloop));
    cmes2("%d iterations\n",iter);
    if (config.optimized && mfoptim & MFdc)
	blockopt(1);			// do block optimization
    if (config.optimized)
#endif
    {
	for (b = startblock; b; b = b->Bnext)
	{
	    if (b->Belem)
	    {   postoptelem(b->Belem);
	    }
	}
	if (mfoptim & MFvbe)
	    verybusyexp();		/* very busy expressions	 */
	if (mfoptim & MFcse)
	    builddags();		/* common subexpressions	 */
	if (mfoptim & MFdv)
	    deadvar();			/* eliminate dead variables	 */
    }
#ifdef DEBUG
    if (debugb)
    {
	dbg_printf(".............After optimization...........\n");
	WRfunc();
    }
#endif

    // Prepare for code generator
    for (b = startblock; b; b = b->Bnext)
    {
	block_optimizer_free(b);
    }
#endif
}

#endif // !SPP
